package com.yanggu.redisson.controller;

import com.yanggu.redisson.domain.OrderData;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.redisson.api.geo.GeoSearchArgs;
import org.redisson.api.geo.OptionalGeoSearch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 测试使用Redisson的对象型数据结构
 */
@Slf4j
@RestController
@RequestMapping("/test-object")
public class TestObjectController {

    @Autowired
    @Qualifier("test-bucket")
    private RBucket<Integer> bucket;

    @Autowired
    @Qualifier("test-bucket2")
    private RBucket<OrderData> bucket2;

    @Autowired
    @Qualifier("test-geo")
    private RGeo<String> geo;

    @Autowired
    @Qualifier("test-rate-limiter")
    private RRateLimiter rateLimiter;

    //底层为Redis的String类型
    @GetMapping("/test1")
    public void test1() {
        bucket.set(1);
        System.out.println(bucket.get());
        //设置过期时间为10s
        bucket.set(2, Duration.ofSeconds(10L));
        System.out.println(bucket.get());
    }

    @GetMapping("/test2")
    public void test2() {
        bucket2.set(new OrderData(1L, "2020-01-01 00:00:00"));
        System.out.println(bucket2.get());
    }

    //底层为Redis的Geo类型
    @GetMapping("/test3")
    public void test3() {
        GeoEntry palermo = new GeoEntry(13.361389, 38.115556, "Palermo");
        GeoEntry catatonia = new GeoEntry(15.087269, 37.502669, "Catatonia");
        geo.add(palermo, catatonia);

        //返回两点之间的距离
        Double distance = geo.dist("Palermo", "Catatonia", GeoUnit.KILOMETERS);
        System.out.println("distance = " + distance);

        //根据点返回坐标信息
        Map<String, GeoPosition> pos = geo.pos("Palermo", "Catatonia", "test1");
        System.out.println("pos = " + pos);

        //地理查询参数。以给定的坐标点作为中心，查询半径为200公里，返回10个点
        OptionalGeoSearch geoSearch = GeoSearchArgs
                //中心点坐标
                .from(15, 37)
                //半径
                .radius(200, GeoUnit.KILOMETERS)
                //排序
                .order(GeoOrder.ASC)
                //限制数量
                .count(10);

        //返回点名称
        List<String> cities = geo.search(geoSearch);
        System.out.println("cities = " + cities);

        //返回点和坐标
        Map<String, GeoPosition> positionMap = geo.searchWithPosition(geoSearch);
        System.out.println("positionMap = " + positionMap);

        //返回点和距离
        Map<String, Double> distancesMap = geo.searchWithDistance(geoSearch);
        System.out.println("distancesMap = " + distancesMap);

        //返回点名称、坐标、距离
        Map<String, GeoPosition> posMap = geo.pos(distancesMap.keySet().toArray(new String[0]));
        Map<String, GeoResult> resultMap = new HashMap<>();
        distancesMap.forEach((point, tempDistance) -> {
            GeoPosition geoPosition = posMap.get(point);
            GeoResult geoResult = new GeoResult();
            geoResult.setName(point);
            geoResult.setPosition(geoPosition);
            geoResult.setDistance(tempDistance);
            resultMap.put(point, geoResult);
        });

        System.out.println("resultMap = " + resultMap);
    }

    //测试RateLimiter。基于令牌桶算法实现
    @GetMapping("/test4")
    public void test4() {
        //设置限流规则。2s内生成2个令牌
        rateLimiter.setRate(RateType.OVERALL, 2L, Duration.ofSeconds(2L));
        boolean tryAcquire = false;
        try {
            //获取3个令牌，如果获取不到并不是返回false，而是会抛出异常，需要手动catch住
            tryAcquire = rateLimiter.tryAcquire(3L);
        } catch (Exception e) {
            log.error("Error processing redisson rate limiter message", e);
        }
        System.out.println("tryAcquire = " + tryAcquire);
        //获取两个令牌。等待2s
        tryAcquire = rateLimiter.tryAcquire(2L, Duration.ofSeconds(2L));
        System.out.println("tryAcquire = " + tryAcquire);
    }

    @Data
    static class GeoResult {

        private String name;

        private GeoPosition position;

        private Double distance;
    }

}
